import * as React from "react";
import * as ReactDOM from "react-dom";
import * as classNames from "classnames";
import nodeShape from './nodeShape';
import * as objectWithoutProperties from 'object-without-properties';
import * as  shallowEqual from 'fbjs/lib/shallowEqual';

const PropTypes = React.PropTypes;
function setCursorPosition(elem, start, index) {
  var val = elem.value
  var len = val.length
  if (index == -1) {
    index = len;
  }

  // 非input和textarea直接返回
  if (elem.nodeName != "INPUT" && elem.nodeName != "TEXTAREA") return
  // 超过文本长度直接返回
  if (len < index) return

  setTimeout(function () {
    elem.focus()
    if (elem.setSelectionRange) { // 标准浏览器
      elem.setSelectionRange(start, index)
    } else { // IE9-
      var range = elem.createTextRange()
      range.moveStart("character", -len)
      range.moveEnd("character", -len)
      range.moveStart("character", index)
      range.moveEnd("character", 0)
      range.select()
    }
  }, 1)
}
class TreeNode extends React.PureComponent<any, any> {
  static propTypes = {
    // checked: PropTypes.number.isRequired,
    disabled: PropTypes.bool.isRequired,
    expandDisabled: PropTypes.bool.isRequired,
    expanded: PropTypes.bool.isRequired,
    label: PropTypes.string.isRequired,
    // optimisticToggle: PropTypes.bool.isRequired,
    showNodeIcon: PropTypes.bool.isRequired,
    treeId: PropTypes.string.isRequired,
    value: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number,
    ]).isRequired,
    // onCheck: PropTypes.func.isRequired,
    onExpand: PropTypes.func.isRequired,
    onClickLabel: PropTypes.func.isRequired,
    getExtraNode: PropTypes.func.isRequired,
    onFinishedEdit: PropTypes.func.isRequired,
    children: PropTypes.node,
    className: PropTypes.string,
    icon: PropTypes.node,
    rawChildren: PropTypes.arrayOf(nodeShape),
  };

  static defaultProps = {
    children: null,
    className: null,
    icon: null,
    rawChildren: null,
  };

  private labelRef;

  constructor(props) {
    super(props);

    this.state = {
      hover: false
    }

    //this.onCheck = this.onCheck.bind(this);
    this.onExpand = this.onExpand.bind(this);
    this.onClickLabel = this.onClickLabel.bind(this);
    this.onMouseEnter = this.onMouseEnter.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
    this.onSelect = this.onSelect.bind(this);
  }


  shouldComponentUpdate(nextProps, nextState) {
    return !shallowEqual(objectWithoutProperties(this.props, "rawChildren"), objectWithoutProperties(nextProps, "rawChildren"))
      || !shallowEqual(this.state, nextState)
  }

  componentDidMount() {
    if (this.props.editable) {
      setCursorPosition(ReactDOM.findDOMNode(this.refs.input), 0, -1);
    }
  }

  componentDidUpdate() {
    if (this.props.editable) {
      setCursorPosition(ReactDOM.findDOMNode(this.refs.input), 0, -1);
    }
  }

  onCheck() {
    let isChecked = false;

    // Toggle off state to checked
    if (this.props.checked === 0) {
      isChecked = true;
    }

    // Toggle partial state based on cascade model
    if (this.props.checked === 2) {
      isChecked = this.props.optimisticToggle;
    }

    this.props.onCheck({
      value: this.props.value,
      checked: isChecked,
      children: this.props.rawChildren,
    });
  }
  onExpand() {
    this.props.onExpand({
      value: this.props.value,
      expanded: !this.props.expanded,
    });
  }

  onClickLabel() {
    this.props.onClickLabel({
      value: this.props.value,
      children: this.props.rawChildren,
      label: this.props.label
    })
  }


  hasChildren() {
    return this.props.rawChildren !== null;
  }

  onMouseEnter() {
    this.setState({ hover: true })
  }
  onMouseLeave() {
    this.setState({ hover: false })
  }

  onBlur(e) {
    this.props.onFinishedEdit({
      value: this.props.value,
      label: e.target.value
    })
  }

  onKeyDown(e) {
    if (e.keyCode == 13) {
      this.props.onFinishedEdit({
        value: this.props.value,
        label: e.target.value
      })
    }
  }

  renderCollapseIcon() {
    const { expandDisabled, expanded, level, lastest } = this.props;
    const hasChildren = this.hasChildren();
    const className = classNames("button switch", {
      roots_open: expanded && level == 0 && hasChildren,
      center_open: expanded && level != 0 && hasChildren,
      roots_close: !expanded && level == 0 && hasChildren,
      center_close: !expanded && level != 0 && hasChildren,
      center_docu: !lastest && !hasChildren,
      bottom_docu: lastest && !hasChildren,
    })
    return (
      <span
        onClick={this.onExpand}
        className={className}></span>
    );
  }

  renderNodeIcon() {
    if (this.props.icon !== null) {
      return this.props.icon;
    }

    if (!this.hasChildren()) {
      return <span className="button ico_docu" />;
    }

    if (!this.props.expanded) {
      return <span className="button ico_close" />;
    }

    return <span className="button ico_open" />;
  }

  onSelect(e) {
    const { editable } = this.props;
    return editable;
  }


  renderLabel() {
    const { label, selected, value, editable, getExtraNode } = this.props;
    const className = classNames({ curSelectedNode: selected })
    const hover = this.state.hover;
    return (
      <a ref="label" href='javascript:void(0)' title={label}
        className={className}
        onClick={this.onClickLabel}
        onMouseEnter={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
        draggable={false}
        onDragStart={() => false}>
        {this.renderNodeIcon()}
        <span className="node_name" onSelect={this.onSelect}>
          {editable ? <input ref="input" type="text" className="rename" defaultValue={label}
            onBlur={this.onBlur}
            onKeyDown={this.onKeyDown}></input> : label}
        </span>
        {getExtraNode({label, value, selected, editable, hover })}
      </a>)
  }

  renderChildren() {
    if (!this.props.expanded) {
      return null;
    }

    return (
      <ul className="line">
        {
          this.props.children
        }
      </ul>
    )
  }

  render() {
    const { checked, className, disabled, treeId, label, showNodeIcon, value } = this.props;
    const inputId = `${treeId}-${value}`;
    const nodeClass = classNames({
      'rct-node': true,
      'rct-node-parent': this.hasChildren(),
      'rct-node-leaf': !this.hasChildren(),
    }, className);

    return (
      <li className={nodeClass}>
        {this.renderCollapseIcon()}
        {this.renderLabel()}
        {this.renderChildren()}
      </li>
    );
  }

}

export default TreeNode;
